# coding: utf-8
from ..op_basic import as_op
import pandas as pd
import numpy as np
import logging


'''
提取动作特征示例:

df = action[action['type'] == 4] #考虑type4的行动: df的列：user_id, time, type

p_start_time = pd.to_datetime('2016-04-16')
one_delta = np.timedelta64(1, 'D')
max_n_deltas = 15
time_col = 'time'

ts =st.behavior_to_ts_feature(df, ['user_id'], p_start_time, one_delta, max_n_deltas, st.behavior_cal_count, time_col='time')()
result = st.ts_window_row_apply(ts, [np.sum, np.average, np.std], [7, 10, 15], prefix='action4')()

'''

def behavior_cal_count(x, consider_start_time, one_delta, n_deltas, index, time_col='time'):
    '''
    在behavior_to_ts_feature使用。
    表示一个delta内的行为总次数
    '''
    t = [0] * n_deltas
    for time in getattr(x, time_col):
        delta_ = int((time - consider_start_time) / one_delta)
        t[delta_] += 1
    return pd.Series(np.array(t), index=index)


def behavior_cal_has(x, consider_start_time, one_delta, n_deltas, index, time_col='time'):
    '''
    在behavior_to_ts_feature使用。
    表示一个delta内的有无行为。
    '''
    t = [0] * n_deltas
    for time in getattr(x, time_col):
        delta_ = int((time - consider_start_time) / one_delta)
        t[delta_] = 1
    return pd.Series(np.array(t), index=index)


def behavior_cal_has_dup(x, consider_start_time, one_delta, n_deltas, index, time_col='time'):
    '''
    在behavior_to_ts_feature使用。
    表示一个delta内的有无重复行为。
    '''
    t = [0] * n_deltas
    for time in getattr(x, time_col):
        delta_ = int((time - consider_start_time) / one_delta)
        t[delta_] += 1
    for i in range(len(t)):
        if t[i] >= 2:
            t[i] = 1
        else:
            t[i] = 0
    return pd.Series(np.array(t), index=index)

@as_op('behavior_to_ts_feature')
def behavior_to_ts_feature(df, index_col, p_start_time, one_delta, max_n_deltas, func=behavior_cal_count, time_col='time'):
    '''
    将行为数据转换为标准的时间序列。

    df：有一个time_col表示发生时间的行为。index_col是按这个为单位考察。其他行都会忽略。

    time_col：df[time_col]为时间戳，表示行为。
    index_col：按这个进行考察。即按index_col进行分组。最后转换成的时间序列就是这个index_col组成的group的行为总次数/是否行为/...
    p_start_time：准确的预测开始时间，需要为pd.Timestamp类型，如pd.to_datetime('2016-04-16')
    one_delta：考察的resolution， 需要为np.timedelta64类型，常用的考察周期为一天，即np.timedelta64(1, 'D')
    max_n_deltas：最大的one_delta时间。最终我们会过滤df，保留 >= p_start_time - max_n_deltas*one_delta和< p_start_time的行为（>=和<都是严格的）
    func：如何考察每个delta中的行为。常用的__cal_count（在这个delta中总行为次数）， __cal_has（在这个delta中是否有行为）， __cal_dup（在这个delta中是否重复行为）
                自定义func参考：__cal_count(x, consider_start_time, one_delta, n_deltas, index, time_col='time')，返回对应index的series

    返回形式：
        row：index为按index_col group之后的结果
        columns：每个delta的start时间
    '''
    consider_start_time = p_start_time - max_n_deltas * one_delta
    df = df[(getattr(df, time_col) >= consider_start_time) & (getattr(df, time_col) < p_start_time)]
    columns_name = [consider_start_time + one_delta * i for i in range(0, max_n_deltas)]
    date_index = pd.DatetimeIndex(columns_name)
    date_index.name = 'delta_start'
    return df.groupby(index_col).apply(func, consider_start_time, one_delta, max_n_deltas, date_index, time_col)

@as_op('behavior_to_ts_feature_report')
def behavior_to_ts_feature_report(df, index_col, p_start_time, one_delta, max_n_deltas, func=behavior_cal_count, time_col='time', report_step=1000):
    '''
    和behavior_to_ts_feature一样。这个是拆开做，并加入report_step表示每做那么多组报告一下进度。
    '''
    result = []
    consider_start_time = p_start_time - max_n_deltas * one_delta
    df = df[(getattr(df, time_col) >= consider_start_time) & (getattr(df, time_col) < p_start_time)]
    consider_start_time = p_start_time - max_n_deltas * one_delta
    columns_name = [consider_start_time + one_delta * i for i in range(0, max_n_deltas)]
    date_index = pd.DatetimeIndex(columns_name)
    date_index.name = 'delta_start'
    grouped = df.groupby(index_col)
    count = 0
    all_group_num = len(grouped)
    for name, group in grouped:
        count += 1
        if count % report_step == 0:
            logging.warning('%d / %d' % (count, all_group_num))
        result.append(func(group, consider_start_time, one_delta, max_n_deltas, columns_name, time_col))
    index = grouped.apply(lambda x: 0).index  # hack。获得group的index
    result = pd.DataFrame(result, columns=date_index, index=index)
    return result
